import type { AxiosInstance, AxiosResponse } from 'axios'
import { IAxiosRequestConfig } from './types'
import { TOKEN_LOGIN_KEY, TOKEN_STORE_KEY } from '@/enums/cacheEnum'
import { local_storage } from '@/utils/storage'

import axios from 'axios'
import { AxiosCanceler } from './axiosCancel'
import { isFunction } from '@/utils/is'
import { cloneDeep } from 'lodash-es'

import type { RequestOptions, CreateAxiosOptions, Result } from './types'
// import { ContentTypeEnum } from '/@/enums/httpEnum';

export * from './axiosTransform'

/**
 * @description:  axios模块
 */
export class VAxios {
  private axiosInstance: AxiosInstance
  private options: CreateAxiosOptions

  constructor(options: CreateAxiosOptions) {
    this.options = options
    this.axiosInstance = axios.create(options)
    this.setupInterceptors()
  }

  /**
   * @description:  创建axios实例
   */
  private createAxios(config: CreateAxiosOptions): void {
    this.axiosInstance = axios.create(config)
  }

  private getTransform() {
    const { transform } = this.options
    return transform
  }

  getAxios(): AxiosInstance {
    return this.axiosInstance
  }

  /**
   * @description: 重新配置axios
   */
  configAxios(config: CreateAxiosOptions) {
    if (!this.axiosInstance) {
      return
    }
    this.createAxios(config)
  }

  /**
   * @description: 设置通用header
   */
  setHeader(headers: any): void {
    if (!this.axiosInstance) {
      return
    }
    Object.assign(this.axiosInstance.defaults.headers, headers)
  }

  /**
   * @description: 拦截器配置
   */
  private setupInterceptors() {
    const transform = this.getTransform()
    if (!transform) {
      return
    }
    const { requestInterceptors, requestInterceptorsCatch, responseInterceptors, responseInterceptorsCatch } = transform

    const axiosCanceler = new AxiosCanceler()

    // 请求拦截器配置处理
    this.axiosInstance.interceptors.request.use((config: IAxiosRequestConfig) => {
      const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config
      !ignoreCancelToken && axiosCanceler.addPending(config)
      if (requestInterceptors && isFunction(requestInterceptors)) {
        config = requestInterceptors(config)
      }
      return config
    }, undefined)

    // 请求拦截器错误捕获
    requestInterceptorsCatch &&
      isFunction(requestInterceptorsCatch) &&
      this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch)

    // 响应结果拦截器处理
    this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
      res && axiosCanceler.removePending(res.config)
      if (responseInterceptors && isFunction(responseInterceptors)) {
        res = responseInterceptors(res)
      }
      return res
    }, undefined)

    // 响应结果拦截器错误捕获
    responseInterceptorsCatch &&
      isFunction(responseInterceptorsCatch) &&
      this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch)
  }

  /**
   * @description:  文件上传
   */
  uploadFiles(config: any, file: any) {
    return new Promise((resolve, reject) => {
      const { $message: Message, $dialog: Dialog } = window as any
      const token = local_storage.get(TOKEN_STORE_KEY) || local_storage.get(TOKEN_LOGIN_KEY)
      axios
        .postForm(
          config.url,
          {
            ...(config.data || {}),
            [config.name || 'file']: file
          },
          {
            headers: {
              ...(config.headers || {}),
              accessToken: token
            }
          }
        )
        .then((res) => {
          const data = res.data
          const { code } = data
          if (/^200$/.test(code)) {
            resolve(data)
          } else {
            Message.error(data.msg)
            reject(data)
          }
        })
        .catch((error) => {
          const { response, code, message } = error || {}
          // TODO 此处要根据后端接口返回格式修改
          const msg: string = response && response.data && response.data.msg ? response.data.msg : message || '出错啦'
          const err: string = error.toString()
          try {
            if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
              Message.error('接口请求超时,请刷新页面重试!')
              return
            } else if (err && err.includes('Network Error')) {
              Dialog.info({
                title: '网络异常',
                content: '请检查您的网络连接是否正常!',
                positiveText: '确定',
                onPositiveClick: () => {}
              })
              return
            } else {
              Message.error(msg)
            }
          } catch (error: any) {
            throw new Error(error)
          }
          reject(error)
        })
    })
  }

  /**
   * @description:   请求方法
   */
  request<T = any>(config: IAxiosRequestConfig, options?: RequestOptions): Promise<T> {
    let conf: IAxiosRequestConfig = cloneDeep(config)
    const transform = this.getTransform()

    const { requestOptions } = this.options

    const opt: RequestOptions = Object.assign({}, requestOptions, options)

    const { beforeRequestHook, requestCatch, transformRequestData } = transform || {}
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt)
    }
    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request<any, AxiosResponse<Result>>(conf)
        .then((res: AxiosResponse<Result>) => {
          // 请求是否被取消
          const isCancel = axios.isCancel(res)
          if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
            const ret = transformRequestData(res, opt)
            // ret !== undefined ? resolve(ret) : reject(new Error('request error!'));
            return resolve(ret)
          }
          reject(res as unknown as Promise<T>)
        })
        .catch((e: Error) => {
          if (requestCatch && isFunction(requestCatch)) {
            reject(requestCatch(e))
            return
          }
          reject(e)
        })
    })
  }
}
